/**
 * 服务器程序
 * 升序链表定时器
 * build:
 * $ g++ close_inactive_conn.cpp
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <string.h>
#include <unistd.h>

#include "lst_timer.h"

#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER  1024
#define TIMESLOT 5

static int pipefd[2];

static sort_timer_lst timer_lst;
static int epollfd = 0;

int setnonblocking(int fd)
{
    int old_opt = fcntl(fd, F_GETFL);
    int new_opt = old_opt | O_NONBLOCK;
    fcntl(fd, F_SETFL, new_opt);
    return old_opt;
}

void addfd(int epollfd, int fd)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET; /* data to read, ET mode */
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
    setnonblocking(fd);
}

void sig_handler(int sig)
{
    /* dont break other reetrant functions */
    int save_errno = errno;
    int msg = sig;
    send(pipefd[1], (char *)&msg, 1, 0); /* write sig no to pipe */
    errno = save_errno;
}

/**
 * register signal
 */
void addsig(int sig)
{
    struct sigaction sa;
    bzero(&sa, sizeof(sa));
    sa.sa_handler = sig_handler;
    sa.sa_flags |= SA_RESTART;
    sigfillset(&sa.sa_mask);
    int ret = sigaction(sig, &sa, NULL);
    assert(ret != -1);
}

/**
 * timer task 
 */
void timer_handler()
{
    /* timer task, i.e. call tick() actually */
    timer_lst.tick();
    /* trigger generating SIGALRM periodly */
    alarm(TIMESLOT);
}

void cb_func(client_data * user_data)
{
    /* remove user sockfd from epoll instance */
    epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);
    assert(user_data);
    close(user_data->sockfd);
    printf("close fd %d\n", user_data->sockfd);
}

int main(int argc, char *argv[])
{
    if (argc <= 2) {
        fprintf(stderr, "usage: %s ip_address port_number\n", argv[0]);
        exit(1);
    }

    const char *ip = argv[1];
    int port = atoi(argv[2]);

    int ret = 0;
    struct sockaddr_in servaddr;
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &servaddr.sin_addr);
    servaddr.sin_port = htons(port);

    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    assert(listenfd >= 0);

    ret = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    assert(ret != -1);

    ret = listen(listenfd, 5);
    assert(ret != -1);

    struct epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    assert(epollfd != -1);
    addfd(epollfd, listenfd);

    /* create a pair of nameless pipe using Unix Domain Socket */
    ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
    assert(ret != -1);

    setnonblocking(pipefd[1]);
    addfd(epollfd, pipefd[0]);

    /* set signal handler */
    addsig(SIGALRM);
    addsig(SIGTERM);
    bool stop_server = false;

    struct client_data *users = new client_data[FD_LIMIT];
    bool timeout = false;
    alarm(TIMESLOT); /* set alarm timer */

    while (!stop_server) {
        int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
        if ((number < 0) && (errno != EINTR)) {
            printf("epoll failure\n");
            break;
        }

        for (int i = 0; i < number; ++i) {
            int sockfd = events[i].data.fd;
            /* process new user connection */
            if (sockfd == listenfd) {
                struct sockaddr_in cliaddr;
                socklen_t clilen = sizeof(cliaddr);
                int connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
                addfd(epollfd, connfd);
                users[connfd].address = cliaddr;
                users[connfd].sockfd = connfd;

                /* create timer, config call back and timeout, then bind timer and user data. add the timer to linked list timer_lst at last */
                util_timer *timer = new util_timer;
                timer->user_data = &users[connfd]; /* bind user data */
                timer->cb_func = cb_func; /* call back function */
                time_t cur = time(NULL);
                timer->expire = cur + 3 * TIMESLOT; /* timeout */
                users[connfd].timer = timer; /* timer */

                timer_lst.add_timer(timer); /* add timer to the linked list */
            }
            /* handle signal */
            else if((sockfd == pipefd[0]) && (events[i].events & EPOLLIN)){
                int sig;
                char signals[1024];
                ret = recv(pipefd[0], signals, sizeof(signals), 0);
                if (ret == -1) { /* error */
                    continue;
                }
                else if (ret == 0) {
                    continue;
                }
                else {
                    for (int i = 0; i < ret; i++) {
                       switch (signals[i]) {
                        case SIGALRM:
                        {/* mark there is timer task to handle using timeout */
                            timeout = true;
                            break;
                        }
                        case SIGTERM:
                        {
                            stop_server = true;
                        }
                       }
                    }
                }
            }
            /* handle received data from user connection */
            else if (events[i].events & EPOLLIN) {
                bzero(users[sockfd].buf, sizeof(users[sockfd].buf));
                ret = recv(sockfd, users[sockfd].buf, BUFFER_SIZE - 1, 0);
                printf("get %d bytes of client data %s from %d\n", 
                ret, 
                users[sockfd].buf, sockfd);

                util_timer* timer = users[sockfd].timer;
                if (ret < 0) {
                    /* close conn and remove timer if read error */
                    if (errno != EAGAIN) { /* no data to read but timeout when reading nonblock fd*/
                        cb_func(&users[sockfd]);
                        if (timer) timer_lst.del_timer(timer);
                    }
                }
                else if (ret == 0) {
                    /* close conn if peer close or shutdown */
                    cb_func(&users[sockfd]);
                    if (timer) timer_lst.del_timer(timer);
                }
                else {
                    /* there is data to read, we need to adjust the timer to increase the expire of the timer */
                    if (timer) {
                        time_t cur = time(NULL);
                        timer->expire = cur + 3 * TIMESLOT; /* refresh expire */
                        printf("adjust timer once\n");
                        timer_lst.adjust_timer(timer);
                    }
                }
            }
            else {
                // other events on epoll
            }
        }

        /* handle timeout task at last becuase I/O events own higher priority */
        if (timeout) {
            timer_handler();
            timeout = false;
        }
    }

    close(listenfd);
    close(pipefd[1]);
    close(pipefd[0]);
    delete[] users;

    return 0;
}


